//
// Created by DELL on 2023/12/13.
//

#include <iostream>
#include <utility>
#include <deque>
#include "WglContext.h"


class MessageQueue
{
public:
    using Call = std::function<void(void)>;

    void enqueue(const Call& msg)
    {
        std::unique_lock<std::mutex> lock(guard);
        this->queue.push_back(msg);
        if (this->queue.size() > 5)
        {
            this->queue.pop_front();
        }
        signal.notify_one();
    }

    Call dequeue()
    {
        // unique_lock 会锁定锁，并在析构时释放锁
        std::unique_lock<std::mutex> lock(guard);
        // wait 如果进入等待会释放锁
        signal.wait(lock, [this]() { return !this->queue.empty(); });
        // wait 结束后重新锁定锁，获得锁，
        auto call = std::move(this->queue.front());
        this->queue.pop_front();
        return call;
    }

private:

    std::deque<Call> queue;
    std::condition_variable signal;
    std::mutex guard;
};




GLuint shader, vao, vbo;
constexpr GLfloat vert[] = { 0.0f, 0.5f,  -0.5f, -0.5f,   0.5f, -0.5f};
constexpr const char* vertShaderSrc = R"(
#version 430 core
layout(location=0) in vec2 pos;
layout(location=0) uniform float scale;
layout(location=1) uniform float rotate;
void main()
{
    vec2 v = pos * scale;
    vec2 v2;
    v2.x = v.x * cos(rotate) - v.y * sin(rotate);
    v2.y = v.x * sin(rotate) + v.y * cos(rotate);
	gl_Position = vec4(v2, 0.0, 1.0);
}
)";
constexpr const char* fragShaderSrc = R"(
#version 430 core
layout(location=2)  uniform vec4 color;
out vec4 outColor;
void main()
{
	outColor = color;
}
)";
constexpr float pi = 3.1415926f;
auto startTime = std::chrono::steady_clock::now();

void draw(const std::shared_ptr<WglContext>& context)
{
    auto time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - startTime).count();
    float rotate = float(time % 800000) / 2000.0f;

    glClearColor(0.3f, 0.5f, 0.6f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(shader);
    glUniform1f(0 , 1.0f);
    glUniform1f(1 , rotate);
    glUniform4f(2, 1.0f, 0.0f, 0.0f, 1.0f);
    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    context->swapBuffer();
}

int main()
{
    std::cout << "OpenGL Queue" << std::endl;

    auto context = WglContext::create(800, 600);
    context->setSwapInterval(1); // 开启垂直同步
    context->bind();
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(0);

    shader = glCreateProgram();
    GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertShader, 1, &vertShaderSrc, nullptr);
    glCompileShader(vertShader);
    glAttachShader(shader, vertShader);
    GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, &fragShaderSrc, nullptr);
    glCompileShader(fragShader);
    glAttachShader(shader, fragShader);
    glLinkProgram(shader);
    glDeleteShader(vertShader);
    glDeleteShader(fragShader);
    context->unbind();

    MessageQueue queue;
    std::atomic_bool isRunning = true;

    context->setResizeCallback([&context, &queue](int w, int h){
        queue.enqueue([&context, w, h](){
            glViewport(0, 0, w, h);
            draw(context); // 更新画面
        });
    });

    std::thread renderThread([&context, &queue, &isRunning](){
        context->bind();
        while (isRunning)
        {
            auto call = queue.dequeue();
            call();
        }
        context->unbind();
    });

    startTime = std::chrono::steady_clock::now();
    while (context->update())
    {
        queue.enqueue([&context](){
            draw(context);
        });
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    isRunning = false;
    queue.enqueue([](){});
    renderThread.join();
    context.reset();

    return 0;
}


